# TODO: "console %f", xxx
# the place where I normally find 0xffff after an expression is 0x0500 followed by the string "xxx"
# This implies that the 0xffff is actually an indication of combination.
# Need to read it before finishing an expression.

import os, sys
import struct
from StringIO import StringIO
from anoxtools import APE
import win32api, win32con, pywintypes, win32ui

REG_KEY_NAME = "SOFTWARE\\Eidos Interactive\\Anachronox"
DATA_DIR_NAME = "anoxdata"

# All these values are
bitHeader = (317, 0xFFFFFFFFL)

def DecompileFile(fileName, oldStdout):
    try:
        f = open(fileName, 'rb')
    except IOError:
        oldStdout.write("** File not found: %s\n" % fileName)
        sys.exit(1)

    try:
        v = struct.unpack("LL", f.read(8))
        if v == bitHeader:
            decompiler = APE.APEDecompiler(f)
            # decompiler.Read(f)
            APE.OutputAPECode(decompiler.windows, decompiler.switches, decompiler.switchesByLabel)
        else:
            oldStdout.write("** Not an APE file: %s %s\n" % (fileName, hex(v)))
    except:
        f.close()
        raise

    f.close()

def DecompilePath(inPath, outPath, debugStream):
    idx = inPath.find(DATA_DIR_NAME) + len(DATA_DIR_NAME)

    for fileName in os.listdir(inPath):
        inSubPath = os.path.join(inPath, fileName)
        if os.path.isdir(inSubPath):
            DecompilePath(inSubPath, outPath, debugStream)
        elif fileName.endswith(".ape"):
            outFileName = os.path.join(outPath, fileName)
            debugStream.write("** IN: "+ inPath +"\\"+ fileName +":\n")
            # Install our own stdout replacement.
            out = sys.stdout = StringIO()
            # Decompile the file catching the output.
            try:
                DecompileFile(inSubPath, debugStream)
            except:
                print
                print "#### ERROR ERROR ERROR ####"
                debugStream.write(out.getvalue())
                raise
            s = out.getvalue()
            out.close()

            f = open(outFileName[:-4] +".txt", 'w')
            f.write(s)
            f.close()

            debugStream.write("** OUT: "+ outFileName +" (bytes %d)\n" % len(s))

errorMessage1 = """
Anachronox registry entries do not correspond with expectations.
Send a screenshot of the following to richard.m.tew@gmail.com:

%s"""

errorMessage2 = """
Unable to locate the 'anoxdata' directory in the game installation path:

%s
"""

confirmMessage1 = """
Do you wish to decompile the A.P.E. scripts?

If you choose to proceed, they will be decompiled alongside the
original compiled binaries they are generated from.
"""

if __name__ == '__main__':
    if len(sys.argv) < 2:
        regEntries = {}
        regHandle = win32api.RegConnectRegistry(None, win32con.HKEY_LOCAL_MACHINE)
        keyHandle = win32api.RegOpenKeyEx(regHandle, REG_KEY_NAME, 0, win32con.KEY_ALL_ACCESS)
        idx = 0
        ret = True
        while True:
            try:
                entryName, entryValue, entryType  = win32api.RegEnumValue(keyHandle, idx)
            except pywintypes.error, e:
                if e.args[0] == 259:
                    break
                raise
            regEntries[entryName] = entryValue
            idx += 1
        win32api.RegCloseKey(keyHandle)
        win32api.RegCloseKey(regHandle)

        if not regEntries.has_key("SourcePath"):
            insert = "\n".join([ str(x) for x in regEntries.items() ])
            message = errorMessage1 % insert
            win32ui.MessageBox(message.strip(), "Decompilation Error", win32con.MB_OK)
            sys.exit(1)
        anoxdataPath = regEntries["SourcePath"] +"\\"+ DATA_DIR_NAME
    else:
        anoxdataPath = sys.argv[1]

    if not os.path.isdir(anoxdataPath):
        message = errorMessage2 % anoxdataPath
        win32ui.MessageBox(message.strip(), "Decompilation Error", win32con.MB_OK)
        sys.exit(1)

    ret = win32ui.MessageBox(confirmMessage1.strip(), "Proceed With Decompilation?", win32con.MB_OKCANCEL)
    if ret == win32con.IDCANCEL:
        sys.exit(1)

    if not len(sys.path[0]):
        win32ui.MessageBox("Unable to determine the current path.", "Decompilation Error", win32con.MB_OK)
        sys.exit(1)

    outPath = os.path.join(sys.path[0], "apes-output")
    if os.path.exists(outPath):
        for fileName in os.listdir(outPath):
            filePath = os.path.join(outPath, fileName)
            os.remove(filePath)
    else:
        os.makedirs(outPath)    

    oldStdout = sys.stdout
    try:
        oldStdout.write("anoxdata path = '%s'\n" % anoxdataPath)
        DecompilePath(anoxdataPath, outPath, oldStdout)
    finally:
        sys.stdout = oldStdout
